﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using MediaController.Client.DAL;
using MediaController.Extractor;
using MediaController.Utils.Listener;
using System.Threading;

namespace MediaController.Watcher
{
    public class ArchivesWatcher : WatchAdapter
    {
        #region Private Methods
        private readonly Queue<string> _rarFilesQueue;
        private HashSet<string> _ignoredfiles;
        #endregion

        public bool MoveExtractFile
        {
            get { return ListenersRow.Move; }
            set { ListenersRow.Move = value; }
        }

        public string Exexute
        {
            get { return ListenersRow.Execute; }
            set { ListenersRow.Execute = value; }
        }

        public MediaControllerDataSet.ListenersRow ListenersRow { get; private set; }

        public event Action<object, FondedFileEventArgs> FileFouned;

        public event Action<object, ExtractingFileEventArgs> ExtractingFile;

        public event Action<object, DoneExtractingEventArgs> ExtractCompleted;

        public ArchivesWatcher(MediaControllerDataSet.ListenersRow listenersRow)
            : base(listenersRow.Source)
        {
            ListenersRow = listenersRow;
            _rarFilesQueue = new Queue<string>();
            _ignoredfiles = new HashSet<string>();
        }

        public override void Start()
        {
            base.Start();
            ThreadPool.QueueUserWorkItem(RunArchiveQueue);
        }

        protected override void OnFolderListenerFileReceived(FolderListener folderListener, ReceivedFileInfo receivedFileInfo)
        {
            if ((Regex.Match(receivedFileInfo.FullName, @"^.*\.rar$|^*.001", RegexOptions.IgnoreCase).Success &&
                 !_ignoredfiles.Contains(receivedFileInfo.FullName)) && !AlreadyExtract(receivedFileInfo.FullName))
            {
                RarFileFounded(receivedFileInfo);
            }
            else if (Regex.Match(receivedFileInfo.FullName, @"^.*\.mkv$|^*.avi", RegexOptions.IgnoreCase).Success)
            {
                //VideoFileFounded(receivedFileInfo);
            }
        }

        private void VideoFileFounded(ReceivedFileInfo receivedFileInfo)
        {
            if (AlreadyExtract(receivedFileInfo.FullName)) return;

            var fileEventArgs = new FondedFileEventArgs(receivedFileInfo.FullName);
            InvokeFileFouned(fileEventArgs);
            MarkFileAsDone(receivedFileInfo.FullName);
        }

        private void RarFileFounded(ReceivedFileInfo receivedFileInfo)
        {
            receivedFileInfo.DeleteOnComplete = false;
            var archiveCollection = new ArchiveCollection();
            archiveCollection.MaxDepth = 1;
            archiveCollection.Source = new DirectoryInfo(Path.GetDirectoryName(receivedFileInfo.FullName));

            foreach (var archive in archiveCollection.Archives)
            {
                if (_ignoredfiles.Contains(archive.File.FullName) || AlreadyExtract(archive.File.FullName)) continue;

                try
                {
                    var fileEventArgs = new FondedFileEventArgs(archive.File.FullName);
                    InvokeFileFouned(fileEventArgs);

                    if (!fileEventArgs.Cancel)
                        _rarFilesQueue.Enqueue(archive.File.FullName);
                    else
                        _ignoredfiles.Add(archive.File.FullName);
                }
                catch (Exception ex)
                {
                    Logger.Log(string.Format(ex.ToString()));
                }
            }
        }


        #region Private Methods

        private void RunArchiveQueue(object state)
        {
            while (true)
            {
                if (_rarFilesQueue.Count > 0)
                {
                    var archive = _rarFilesQueue.Peek();
                    InvokeExtractingFile(new ExtractingFileEventArgs(archive));

                    string destination;
                    ArchiveCollection archiveCollection = CreateExtractor(archive, out destination);                    
                    archiveCollection.Process();

                    MarkFileAsDone(archive);

                    var fileEventArgs = new DoneExtractingEventArgs(archive, destination);
                    InvokeExtractCompleted(fileEventArgs);
                    _rarFilesQueue.Dequeue();
                }
                else
                {
                    Thread.Sleep(5000);
                }
            }
        }

        private ArchiveCollection CreateExtractor(string archive, out string destination)
        {
            var archiveCollection = new ArchiveCollection();
            archiveCollection.MaxDepth = 1;
            archiveCollection.AppendSourceName = false;
            archiveCollection.Source = new DirectoryInfo(Path.GetDirectoryName(archive));

            destination = Path.Combine(Path.GetDirectoryName(archive), "Extracted");
            archiveCollection.Destination = new DirectoryInfo(destination);
            return archiveCollection;
        }

        private void MarkFileAsDone(string filename)
        {
            File.Create(GetDoneFileName(filename));
        }

        private bool AlreadyExtract(string filename)
        {
            string fileNameDone = GetDoneFileName(filename);
            if (File.Exists(fileNameDone))
            {
                return true;
            }
            return false;
        }

        private string GetDoneFileName(string filename)
        {
            string foundedFullFileName = filename;
            string foundedFileDir = Path.GetDirectoryName(foundedFullFileName);
            string foundedFileName = Path.GetFileNameWithoutExtension(foundedFullFileName);
            string fileNameDone = string.Format("{0}\\{1}.done", foundedFileDir, foundedFileName);
            return fileNameDone;
        }

        private void InvokeFileFouned(FondedFileEventArgs fileName)
        {
            Action<object, FondedFileEventArgs> handler = FileFouned;
            if (handler != null) handler(this, fileName);
        }

        private void InvokeExtractCompleted(DoneExtractingEventArgs fileName)
        {
            Action<object, DoneExtractingEventArgs> handler = ExtractCompleted;
            if (handler != null) handler(this, fileName);
        }

        protected void InvokeExtractingFile(ExtractingFileEventArgs arg2)
        {
            Action<object, ExtractingFileEventArgs> handler = ExtractingFile;
            if (handler != null) handler(this, arg2);
        }


        #endregion
    }
}
